package com.vaadin.thomas.contenttool.data;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;

public class Value extends AbstractData implements Comparable<Value> {

	public Value(Node n) {
		super(n);
	}

	static final String PROP_ID = "identifier";
	static final String PROP_DELETED = "deleted";

	public Set<Tag> getTags() {
		Set<Tag> tags = new HashSet<Tag>();
		for (Relationship r : node.getRelationships(References.TAG_TO_VALUES)) {
			tags.add(new Tag(r.getStartNode()));
		}
		return tags;
	}

	public void removeTag(Tag t) {

		checkPreview();

		for (Relationship r : node.getRelationships(References.TAG_TO_VALUES)) {
			Tag tag = new Tag(r.getStartNode());

			if (t.equals(tag)) {
				Transaction tx = node.getGraphDatabase().beginTx();
				try {
					r.delete();
					tx.success();
				} catch (RuntimeException e) {
					tx.failure();
					throw e;
				} finally {
					tx.finish();
				}
				break;
			}
		}
	}

	private void checkPreview() {
		if (!getVersion().isPreview()) {
			throw new RuntimeException("only allowed to modify preview version");
		}
	}

	public void addTag(Tag t) {

		checkPreview();
		if (getTags().contains(t)) {
			return;
		}

		Transaction tx = node.getGraphDatabase().beginTx();
		try {
			t.node.createRelationshipTo(node, References.TAG_TO_VALUES);
			tx.success();
		} catch (RuntimeException e) {
			tx.failure();
			throw e;
		} finally {
			tx.finish();
		}
	}

	public String getId() {
		return (String) node.getProperty(PROP_ID);
	}

	public Map<Locale, List<Localization>> getLocalizations() {

		Map<Locale, List<Localization>> m = new HashMap<Locale, List<Localization>>();
		for (Relationship r : node.getRelationships(References.VALUE_TO_LOC)) {
			Localization l = new Localization(r.getEndNode());

			if (m.get(l.getLanguage()) == null) {
				m.put(l.getLanguage(), new ArrayList<Localization>());
			}
			m.get(l.getLanguage()).add(l);
		}
		return m;
	}

	public Map<Locale, List<Localization>> getAllLocalizations() {

		Map<Locale, List<Localization>> m = new HashMap<Locale, List<Localization>>();

		if (getPreviousVersion() != null) {
			m.putAll(getPreviousVersion().getAllLocalizations());
		}
		m.putAll(getLocalizations());
		return m;
	}

	public void addLocalization(Localization loc) {

		checkPreview();

		Transaction tx = node.getGraphDatabase().beginTx();
		try {
			node.createRelationshipTo(loc.node, References.VALUE_TO_LOC);
			tx.success();
		} catch (RuntimeException e) {
			tx.failure();
			throw e;
		} finally {
			tx.finish();
		}
	}

	public Value getPreviousVersion() {
		return getVersion(Direction.INCOMING);
	}

	public Value getNextVersion() {
		return getVersion(Direction.OUTGOING);
	}

	private Value getVersion(Direction d) {
		Relationship r = node.getSingleRelationship(References.VALUE_TO_NEWER,
				d);
		if (r == null) {
			return null;
		}

		Value v = new Value(r.getOtherNode(node));
		return v;
	}

	public Version getVersion() {

		return new Version(node.getSingleRelationship(
				References.VERSION_TO_VALUES, Direction.INCOMING)
				.getStartNode());
	}

	public static Value create(String identifier, Localization... locs) {

		Transaction tx = Model.db.beginTx();

		Value v = null;
		try {
			Node n = Model.db.createNode();
			n.setProperty(PROP_ID, identifier);
			n.setProperty(PROP_DELETED, false);

			for (Localization l : locs) {
				n.createRelationshipTo(l.node, References.VALUE_TO_LOC);
			}

			v = new Value(n);

			tx.success();
		} catch (RuntimeException e) {
			tx.failure();
			throw e;
		} finally {
			tx.finish();
		}

		return v;
	}

	public void markForDelete() {

		checkPreview();

		Transaction tx = node.getGraphDatabase().beginTx();
		try {
			node.setProperty(PROP_DELETED, true);
			tx.success();
		} catch (RuntimeException e) {
			tx.failure();
			throw e;
		} finally {
			tx.finish();
		}
	}

	public boolean isDeleted() {
		return (Boolean) node.getProperty(PROP_DELETED);
	}

	@Override
	public int compareTo(Value other) {
		return getId().compareTo(other.getId());
	}

	public void unDelete() {

		checkPreview();

		Transaction tx = node.getGraphDatabase().beginTx();
		try {
			node.setProperty(PROP_DELETED, false);
			tx.success();
		} catch (RuntimeException e) {
			tx.failure();
			throw e;
		} finally {
			tx.finish();
		}
	}

}
